跳到主要内容

gRPC 消息类型转换

依旧是 protoreflect 包的使用

使用例:

package main

import (
"fmt"
"path/filepath"
"stgrpc/log"
"sync"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/desc/protoparse"
"github.com/jhump/protoreflect/dynamic"
)

var (
globalProtoMap map[string]*desc.FileDescriptor
IsCached = true
lk sync.RWMutex
)

func init() {
globalProtoMap = make(map[string]*desc.FileDescriptor)
}

func getProto(path string) *desc.FileDescriptor {
lk.Lock()
defer lk.Unlock()

if IsCached {
fd, ok := globalProtoMap[path]
if ok {
log.Debugf("getProto path:%v cached \n", path)
return fd
}
}
p := protoparse.Parser{}
fds, err := p.ParseFiles(path)
if err != nil {
log.Error("getProto ParseFiles error:", err)
return nil
}

log.Debug("JsonToPb fd", fds[0])
fd := fds[0]

if IsCached {
globalProtoMap[path] = fd
}

return fd
}

// JsonToPb 传入 proto 文件的 path, proto 中对应的 message.name,js 的原始数据
// 返回生成的 proto.Marshal 的 []byte
// example:
// path := "$PROTOPATH/hello.proto"
// messageName "hello.HelloRequest"
// JsonToPb(path,"hello.HelloRequest", []byte(`{"name":"John"}`)
func JsonToPb(protoPath, messageName string, jsonStr []byte) ([]byte, error) {
log.Debug("JsonToPb protoPath", protoPath)

fd := getProto(protoPath)

msg := fd.FindMessage(messageName)

dymsg := dynamic.NewMessage(msg)
err := dymsg.UnmarshalJSON(jsonStr)

if err != nil {
log.Error("JsonToPb UnmarshalJSON error:", err)
return nil, nil
}

log.Debug("JsonToPb UnmarshalJSON dymsg", dymsg)

any, err := ptypes.MarshalAny(dymsg)
if err != nil {
log.Error("JsonToPb MarshalAny error:", err)
return nil, nil
}
log.Debug("JsonToPb marshal any", any.Value)
return any.Value, nil
}

// PbToJson 传入 proto 的 byte 数据,返回它对应的 json 数据
// example:
// path := "$PROTOPATH/helloworld.proto"
// messageName "hello.HelloRequest"
// jsonByte, err := PbToJson(path, messageName, pbByte)
func PbToJson(protoPath, messageName string, protoData []byte) ([]byte, error) {
log.Debug("PbToJson protoPath", protoPath)
fd := getProto(protoPath)
msg := fd.FindMessage(messageName)
dymsg := dynamic.NewMessage(msg)

err := proto.Unmarshal(protoData, dymsg)
log.Debug("PbToJson Unmarshal err:", err)

jsonByte, err := dymsg.MarshalJSON()
return jsonByte, err
}

// 使用例
func main() {
path, _ := filepath.Abs("./tests/hello.proto")
fmt.Println(JsonToPb(path, "hello.HelloRequest", []byte(`{"name":"John"}`)))
}